import 'package:collection/collection.dart' show IterableExtension;
import 'package:family_expense/features/income_expense/constants/income_expense_type.dart';
import 'package:family_expense/features/income_expense_detail_type/models/income_expense_detail_type.dart';
import 'package:family_expense/features/income_expense_detail_type/services/income_expense_detail_type_service.dart';
import 'package:my_flutter_core/my_flutter_core.dart';

/// Loads and caches income/expense detail types.
///
/// It is the class that manages the cache data.
/// Includes loading, adding, updating, deleting and reset order items in the cache list.
class IncomeExpenseDetailTypeLoader {
  static List<IncomeExpenseDetailType> _expenseDetailTypes = [];
  static List<IncomeExpenseDetailType> _incomeDetailTypes = [];

  // static List<IncomeExpenseDetailType> get expenseDetailTypes {
  //   return _expenseDetailTypes;
  // }

  // static List<IncomeExpenseDetailType> get incomeDetailTypes {
  //   return _incomeDetailTypes;
  // }

  /// Loads the data when data is not ready yet.
  ///
  /// [loadedDetailTypes] is the data provided by other service. If this is not null then we don't have to load it again from server.
  static Future<void> init({
    List<IncomeExpenseDetailType>? loadedDetailTypes,
  }) async {
    // Loads all detail types from server if [loadedDetailTypes] is null.
    final detailTypes =
        loadedDetailTypes ??
        await IncomeExpenseDetailTypeService().queryByAccountGroup();

    // Gets expense detail types.
    _expenseDetailTypes = detailTypes
        .where((p) => p.incomeExpenseType == IncomeExpenseType.expense)
        .toList();

    // Gets income detail types.
    _incomeDetailTypes = detailTypes
        .where((p) => p.incomeExpenseType == IncomeExpenseType.income)
        .toList();
  }

  /// Gets corresponding detail types based on [incomeExpenseType].
  static List<IncomeExpenseDetailType> getDetailTypes(
    String? incomeExpenseType,
  ) {
    switch (incomeExpenseType) {
      case IncomeExpenseType.income:
        return _incomeDetailTypes;
      case IncomeExpenseType.expense:
        return _expenseDetailTypes;
      default:
        return [];
    }
  }

  /// Gets [IncomeExpenseDetailType] by [incomeExpenseType] and [name].
  static IncomeExpenseDetailType? getIncomeExpenseDetailType(
    String? incomeExpenseType,
    String? name,
  ) {
    if (incomeExpenseType.isNotNullOrEmpty && name.isNotNullOrEmpty) {
      final detailTypes = getDetailTypes(incomeExpenseType);

      return detailTypes.isNotEmpty
          ? detailTypes.firstWhereOrNull((p) => p.name == name)
          : null;
    } else {
      return null;
    }
  }

  /// Gets the title of a expense detail type by its name.
  /// Uses orElse parameter to slient 'No Element' error if no match found.
  static String? getExpenseDetailTypeTitle(String name) {
    return _expenseDetailTypes.firstWhereOrNull((p) => p.name == name)?.title;
  }

  /// Gets the subtitle of a expense detail type by its name.
  /// Uses orElse parameter to slient 'No Element' error if no match found.
  static String? getExpenseDetailTypeSubtitle(String name) {
    return _expenseDetailTypes
        .firstWhereOrNull((p) => p.name == name)
        ?.subtitle;
  }

  /// Gets the title of a income detail type by its name.
  /// Uses orElse parameter to slient 'No Element' error if no match found.
  static String? getIncomeDetailTypeTitle(String name) {
    return _incomeDetailTypes.firstWhereOrNull((p) => p.name == name)?.title;
  }

  /// Gets the subtitle of a income detail type by its name.
  /// Uses orElse parameter to slient 'No Element' error if no match found.
  static String? getIncomeDetailTypeSubtitle(String name) {
    return _incomeDetailTypes.firstWhereOrNull((p) => p.name == name)?.subtitle;
  }

  // Clears cache data.
  static void clearCache() {
    _expenseDetailTypes.clear();
    _incomeDetailTypes.clear();
  }

  // Resets the cache detail type list.
  static void reset(
    String incomeExpenseType,
    List<IncomeExpenseDetailType> detailTypes,
  ) {
    if (incomeExpenseType == IncomeExpenseType.expense) {
      _expenseDetailTypes = detailTypes.map((p) => p.clone()).toList();
    } else if (incomeExpenseType == IncomeExpenseType.income) {
      _incomeDetailTypes = detailTypes.map((p) => p.clone()).toList();
    }
  }

  /// Changes the cache list.
  ///
  /// [IncomeExpenseType.expense] for expense detail type list.
  /// [IncomeExpenseType.income] for income detail type list.
  ///
  /// - If [index] is null
  ///   - If [detailType.id] exists in source list means deleting.
  ///   - If [detailType.id] does NOT exist in source list means adding new.
  /// - If [index] is NOT null means updating.
  static void change(
    String incomeExpenseType,
    IncomeExpenseDetailType detailType, {
    int? index,
  }) {
    late List<IncomeExpenseDetailType> source;

    if (incomeExpenseType == IncomeExpenseType.expense) {
      source = _expenseDetailTypes;
    } else if (incomeExpenseType == IncomeExpenseType.income) {
      source = _incomeDetailTypes;
    }

    if (index != null && index >= 0) {
      source[index] = detailType.clone();
    } else {
      final matchedIndex = source.indexWhere((p) => p.id == detailType.id);

      if (matchedIndex == -1) {
        source.add(detailType);
      } else {
        source.removeAt(matchedIndex);
      }
    }
  }
}
